import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Constellations {

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		if (args.length != 0) {
			System.out.print("Usage: java Constellations");
			System.exit(1);
		}
		System.out.print("Enter number of stars for your sky [1-50]: ");
		while (!sc.hasNextInt()) {
			sc.nextLine();
			System.out.println("You did not enter an integer!");
			System.out.print("Enter number of stars for your sky [1-50]: ");
		}
		int numberOfStars = sc.nextInt();
		Queue<StarComponent> starComponentQueue = new LinkedList<StarComponent>();
		StarComponent currComponent = null;
		double starProbability = Config.RANDOM.nextDouble();
		double edgeWeight;
		int [][] sky = new int [Config.SKY_SIZE][Config.SKY_SIZE];
		initializeSkyWithStars(sky, numberOfStars, starProbability, Config.SKY_SIZE, Config.SKY_SIZE);

		for (int i = 0; i < Config.SKY_SIZE; i++) {
			for (int j = 0; j < Config.SKY_SIZE; j++)  {
				if (sky[i][j] == Config.STAR  ||
						sky[i][j] == Config.GRAY1 ||
						sky[i][j] == Config.GRAY2 ||
						sky[i][j] == Config.GRAY3 ||
						sky[i][j] == Config.GRAY4 ) {
					//create a new StarComponent with xval, yval, and brightness
					Config.starComponents.add(new StarComponent(i, j, sky[i][j]));
				}
			}
		}
		int counter = 1;
		while (counter < Config.starComponents.size()) {
			for (int i = counter; i < Config.starComponents.size(); i++) {
				starComponentQueue.offer(Config.starComponents.get(i));
			}

			while (!starComponentQueue.isEmpty()) {
				currComponent = starComponentQueue.remove();
				edgeWeight = distance(currComponent.getXCoord(),
						currComponent.getYCoord(),
						Config.starComponents.get(counter-1).getXCoord(),
						Config.starComponents.get(counter-1).getYCoord());
				Config.edgeList.add(new Edge(Config.starComponents.get(counter-1),
						currComponent, edgeWeight));
			}
			counter++;
		}
		Collections.sort(Config.edgeList);
		
//		System.out.println(Config.starComponents.size());
		
		UnionFind myUnionFind = new UnionFind(Config.starComponents);
		
		kruskal(myUnionFind);
		
//		System.out.println(myUnionFind.count());
		
		graphConstellations(myUnionFind);
		
		Config.VIEWER.showWorld(sky);
	}

	private static void graphConstellations(UnionFind myUnionFind) {
		for (int i = 0; i < myUnionFind.count(); i++) {
			System.out.println(i+"th X Coord" + myUnionFind.getStarComponent(i).getXCoord());
			System.out.println(i+"th Y Coord" + myUnionFind.getStarComponent(i).getYCoord());
		}
	}

	private static void kruskal(UnionFind myUnionFind) {		
		for (int i = 0; i < Config.edgeList.size(); i++) {
			if (Config.edgeList.get(i).getEdgeWeight() > 6) continue;
			// check if A && B have been visited, if so you are about to add a cycle
			if (myUnionFind.connected(Config.edgeList.get(i).getA(), Config.edgeList.get(i).getB())) {
				continue;
			}
			// add the edge weight to Config.maxSpanTreeEdgeList
			Config.maxSpanTreeEdgeList.add(Config.edgeList.get(i));
			// take the union of the sets containing edge A and B
			myUnionFind.union(Config.edgeList.get(i).getA(), Config.edgeList.get(i).getB());
		}
	}

	public static double distance(int x1, int y1, int x2, int y2) {
		return Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
	}

	private static void initializeSkyWithStars(int[][] sky, int numberOfStars, 
			double starProbability, int rows, int columns) {
		int tempNumberOfStars = numberOfStars;
	
		while (tempNumberOfStars > 0) {
			int radiusOfStar = Config.RANDOM.nextInt(5) + 2;
			int a = Config.RANDOM.nextInt(rows);
			int b = Config.RANDOM.nextInt(columns);
	
			sky[a][b] = Config.STAR;
	
			for (int row = a - radiusOfStar; row < a + radiusOfStar+1; row++) {
				for ( int col = b - radiusOfStar; col < b + radiusOfStar+1; col++ ) {
					if ( !(row < 0) && !(row >= Config.SKY_SIZE) && !(col < 0) &&
							!(col >= Config.SKY_SIZE)) {
						double randomProbability = Config.RANDOM.nextDouble();
						if ((randomProbability <= starProbability) && ((row + radiusOfStar == 0)
								|| (col + radiusOfStar == 0))) {
							sky[row][col] = Config.STAR;
						} else if (
								(row == a && col == b-1) ||
								(row == a && col == b+1) ||
								(row == a+1 && col == b) ||
								(row == a-1 && col == b) ||
								(row == a-1 && col == b+1) ||
								(row == a+1 && col == b+1) ||
								(row == a+1 && col == b-1) ||
								(row == a-1 && col == b-1) ){
							randomProbability = Config.RANDOM.nextDouble();
							if (randomProbability <= starProbability) {
								sky[row][col] = Config.GRAY1;
							}
						} else if (
								(row == a && col == b-2) ||
								(row == a && col == b+2) ||
								(row == a+2 && col == b) ||
								(row == a-2 && col == b) ||
								(row == a-2 && col == b+2) ||
								(row == a+2 && col == b+2) ||
								(row == a+2 && col == b-2) ||
								(row == a-2 && col == b-2) ){
							randomProbability = Config.RANDOM.nextDouble();
							if (randomProbability <= starProbability) {
								sky[row][col] = Config.GRAY2;
							}
						} else if (
								(row == a && col == b-3) ||
								(row == a && col == b+3) ||
								(row == a+3 && col == b) ||
								(row == a-3 && col == b) ||
								(row == a-3 && col == b+3) ||
								(row == a+3 && col == b+3) ||
								(row == a+3 && col == b-3) ||
								(row == a-3 && col == b-3) ){
							randomProbability = Config.RANDOM.nextDouble();
							if (randomProbability <= starProbability) {
								sky[row][col] = Config.GRAY3;
							}
						} else if (
								(row == a && col == b-4) ||
								(row == a && col == b+4) ||
								(row == a+4 && col == b) ||
								(row == a-4 && col == b) ||
								(row == a-4 && col == b+4) ||
								(row == a+4 && col == b+4) ||
								(row == a+4 && col == b-4) ||
								(row == a-4 && col == b-4) ){
							randomProbability = Config.RANDOM.nextDouble();
							if (randomProbability <= starProbability) {
								sky[row][col] = Config.GRAY4;
							}
						}
					}
				}
			}
			tempNumberOfStars--;
		}
	}
}